// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/tools/flip_server/http_interface.h"

#include "net/tools/balsa/balsa_frame.h"
#include "net/tools/flip_server/flip_config.h"
#include "net/tools/flip_server/sm_connection.h"
#include "net/tools/flip_server/spdy_util.h"
#include "net/tools/flip_server/url_utilities.h"

namespace net {

HttpSM::HttpSM(SMConnection* connection,
    SMInterface* sm_spdy_interface,
    MemoryCache* memory_cache,
    FlipAcceptor* acceptor)
    : http_framer_(new BalsaFrame)
    , stream_id_(0)
    , server_idx_(-1)
    , connection_(connection)
    , sm_spdy_interface_(sm_spdy_interface)
    , output_list_(connection->output_list())
    , output_ordering_(connection)
    , memory_cache_(connection->memory_cache())
    , acceptor_(acceptor)
{
    http_framer_->set_balsa_visitor(this);
    http_framer_->set_balsa_headers(&headers_);
    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY)
        http_framer_->set_is_request(false);
}
HttpSM::~HttpSM()
{
    Reset();
}

void HttpSM::ProcessBodyData(const char* input, size_t size)
{
    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process Body Data: stream "
                << stream_id_ << ": size " << size;
        sm_spdy_interface_->SendDataFrame(stream_id_, input, size, 0, false);
    }
}

void HttpSM::ProcessHeaders(const BalsaHeaders& headers)
{
    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) {
        std::string host = UrlUtilities::GetUrlHost(headers.GetHeader("Host").as_string());
        std::string method = headers.request_method().as_string();
        VLOG(1) << ACCEPTOR_CLIENT_IDENT
                << "Received Request: " << headers.request_uri().as_string() << " "
                << method;
        std::string filename = EncodeURL(headers.request_uri().as_string(), host, method);
        NewStream(stream_id_, 0, filename);
        stream_id_ += 2;
    } else {
        VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Received Response from "
                << connection_->server_ip_ << ":" << connection_->server_port_
                << " ";
        sm_spdy_interface_->SendSynReply(stream_id_, headers);
    }
}

void HttpSM::MessageDone()
{
    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: MessageDone. Sending EOF: "
                << "stream " << stream_id_;
        sm_spdy_interface_->SendEOF(stream_id_);
    } else {
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: MessageDone.";
    }
}

void HttpSM::HandleHeaderError(BalsaFrame* framer) { HandleError(); }

void HttpSM::HandleChunkingError(BalsaFrame* framer) { HandleError(); }

void HttpSM::HandleBodyError(BalsaFrame* framer) { HandleError(); }

void HttpSM::HandleError()
{
    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Error detected";
}

void HttpSM::AddToOutputOrder(const MemCacheIter& mci)
{
    output_ordering_.AddToOutputOrder(mci);
}

void HttpSM::InitSMInterface(SMInterface* sm_spdy_interface,
    int32_t server_idx)
{
    sm_spdy_interface_ = sm_spdy_interface;
    server_idx_ = server_idx;
}

void HttpSM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
    SMInterface* sm_interface,
    EpollServer* epoll_server,
    int fd,
    std::string server_ip,
    std::string server_port,
    std::string remote_ip,
    bool use_ssl)
{
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Initializing server "
            << "connection.";
    connection_->InitSMConnection(connection_pool,
        sm_interface,
        epoll_server,
        fd,
        server_ip,
        server_port,
        remote_ip,
        use_ssl);
}

size_t HttpSM::ProcessReadInput(const char* data, size_t len)
{
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process read input: stream "
            << stream_id_;
    return http_framer_->ProcessInput(data, len);
}

size_t HttpSM::ProcessWriteInput(const char* data, size_t len)
{
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process write input: size "
            << len << ": stream " << stream_id_;
    char* dataPtr = new char[len];
    memcpy(dataPtr, data, len);
    DataFrame* data_frame = new DataFrame;
    data_frame->data = dataPtr;
    data_frame->size = len;
    data_frame->delete_when_done = true;
    connection_->EnqueueDataFrame(data_frame);
    return len;
}

bool HttpSM::MessageFullyRead() const
{
    return http_framer_->MessageFullyRead();
}

void HttpSM::SetStreamID(uint32_t stream_id)
{
    stream_id_ = stream_id;
}

bool HttpSM::Error() const { return http_framer_->Error(); }

const char* HttpSM::ErrorAsString() const
{
    return BalsaFrameEnums::ErrorCodeToString(http_framer_->ErrorCode());
}

void HttpSM::Reset()
{
    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Reset: stream " << stream_id_;
    http_framer_->Reset();
}

void HttpSM::ResetForNewConnection()
{
    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
        VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Server connection closing "
                << "to: " << connection_->server_ip_ << ":"
                << connection_->server_port_ << " ";
    }
    // Message has not been fully read, either it is incomplete or the
    // server is closing the connection to signal message end.
    if (!MessageFullyRead()) {
        VLOG(2) << "HTTP response closed before end of file detected. "
                << "Sending EOF to spdy.";
        sm_spdy_interface_->SendEOF(stream_id_);
    }
    output_ordering_.Reset();
    http_framer_->Reset();
    if (sm_spdy_interface_) {
        sm_spdy_interface_->ResetForNewInterface(server_idx_);
    }
}

void HttpSM::Cleanup()
{
    if (!(acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER)) {
        VLOG(2) << "HttpSM Request Fully Read; stream_id: " << stream_id_;
        connection_->Cleanup("request complete");
    }
}

int HttpSM::PostAcceptHook() { return 1; }

void HttpSM::NewStream(uint32_t stream_id,
    uint32_t priority,
    const std::string& filename)
{
    MemCacheIter mci;
    mci.stream_id = stream_id;
    mci.priority = priority;
    if (!memory_cache_->AssignFileData(filename, &mci)) {
        // error creating new stream.
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound";
        SendErrorNotFound(stream_id);
    } else {
        AddToOutputOrder(mci);
    }
}

void HttpSM::SendEOF(uint32_t stream_id)
{
    SendEOFImpl(stream_id);
    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
        sm_spdy_interface_->ResetForNewInterface(server_idx_);
    }
}

void HttpSM::SendErrorNotFound(uint32_t stream_id)
{
    SendErrorNotFoundImpl(stream_id);
}

size_t HttpSM::SendSynStream(uint32_t stream_id, const BalsaHeaders& headers)
{
    return 0;
}

size_t HttpSM::SendSynReply(uint32_t stream_id, const BalsaHeaders& headers)
{
    return SendSynReplyImpl(stream_id, headers);
}

void HttpSM::SendDataFrame(uint32_t stream_id,
    const char* data,
    int64_t len,
    uint32_t flags,
    bool compress)
{
    SendDataFrameImpl(stream_id, data, len, flags, compress);
}

void HttpSM::SendEOFImpl(uint32_t stream_id)
{
    DataFrame* df = new DataFrame;
    df->data = "0\r\n\r\n";
    df->size = 5;
    df->delete_when_done = false;
    EnqueueDataFrame(df);
    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) {
        Reset();
    }
}

void HttpSM::SendErrorNotFoundImpl(uint32_t stream_id)
{
    BalsaHeaders my_headers;
    my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
    my_headers.RemoveAllOfHeader("content-length");
    my_headers.AppendHeader("transfer-encoding", "chunked");
    SendSynReplyImpl(stream_id, my_headers);
    SendDataFrame(stream_id, "page not found", 14, 0, false);
    SendEOFImpl(stream_id);
    output_ordering_.RemoveStreamId(stream_id);
}

size_t HttpSM::SendSynReplyImpl(uint32_t stream_id,
    const BalsaHeaders& headers)
{
    SimpleBuffer sb;
    headers.WriteHeaderAndEndingToBuffer(&sb);
    DataFrame* df = new DataFrame;
    df->size = sb.ReadableBytes();
    char* buffer = new char[df->size];
    df->data = buffer;
    df->delete_when_done = true;
    sb.Read(buffer, df->size);
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending HTTP Reply header "
            << stream_id_;
    size_t df_size = df->size;
    EnqueueDataFrame(df);
    return df_size;
}

size_t HttpSM::SendSynStreamImpl(uint32_t stream_id,
    const BalsaHeaders& headers)
{
    SimpleBuffer sb;
    headers.WriteHeaderAndEndingToBuffer(&sb);
    DataFrame* df = new DataFrame;
    df->size = sb.ReadableBytes();
    char* buffer = new char[df->size];
    df->data = buffer;
    df->delete_when_done = true;
    sb.Read(buffer, df->size);
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending HTTP Reply header "
            << stream_id_;
    size_t df_size = df->size;
    EnqueueDataFrame(df);
    return df_size;
}

void HttpSM::SendDataFrameImpl(uint32_t stream_id,
    const char* data,
    int64_t len,
    uint32_t flags,
    bool compress)
{
    char chunk_buf[128];
    snprintf(chunk_buf, sizeof(chunk_buf), "%x\r\n", (unsigned int)len);
    std::string chunk_description(chunk_buf);
    DataFrame* df = new DataFrame;
    df->size = chunk_description.size() + len + 2;
    char* buffer = new char[df->size];
    df->data = buffer;
    df->delete_when_done = true;
    memcpy(buffer, chunk_description.data(), chunk_description.size());
    memcpy(buffer + chunk_description.size(), data, len);
    memcpy(buffer + chunk_description.size() + len, "\r\n", 2);
    EnqueueDataFrame(df);
}

void HttpSM::EnqueueDataFrame(DataFrame* df)
{
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Enqueue data frame: stream "
            << stream_id_;
    connection_->EnqueueDataFrame(df);
}

void HttpSM::GetOutput()
{
    MemCacheIter* mci = output_ordering_.GetIter();
    if (mci == NULL) {
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput: nothing to "
                << "output!?: stream " << stream_id_;
        return;
    }
    if (!mci->transformed_header) {
        mci->bytes_sent = SendSynReply(mci->stream_id, *(mci->file_data->headers()));
        mci->transformed_header = true;
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput transformed "
                << "header stream_id: [" << mci->stream_id << "]";
        return;
    }
    if (mci->body_bytes_consumed >= mci->file_data->body().size()) {
        SendEOF(mci->stream_id);
        output_ordering_.RemoveStreamId(mci->stream_id);
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "GetOutput remove_stream_id: ["
                << mci->stream_id << "]";
        return;
    }
    size_t num_to_write = mci->file_data->body().size() - mci->body_bytes_consumed;
    if (num_to_write > mci->max_segment_size)
        num_to_write = mci->max_segment_size;

    SendDataFrame(mci->stream_id,
        mci->file_data->body().data() + mci->body_bytes_consumed,
        num_to_write,
        0,
        true);
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput SendDataFrame["
            << mci->stream_id << "]: " << num_to_write;
    mci->body_bytes_consumed += num_to_write;
    mci->bytes_sent += num_to_write;
}

} // namespace net
